/**
 * 
 */
package org.zgstudio.ddlchecker;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.LinkedList;
import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.LineIterator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 * @author zhguo
 * 
 */
public class DeliciousPostsMgrV1 {
    private static String baseURLV1       = "https://api.del.icio.us/v1";
    private static String pathRecentPosts = "/posts/recent";
    private static String pathAllPosts    = "/posts/all";
    private static String pathDelPost     = "/posts/delete";
    
    private String username;
    private String password;
    
    public DeliciousPostsMgrV1(String username, String password) {
        this.setUsername(username);
        this.setPassword(password);
    }
    public DeliciousPostsMgrV1 () {}
    
    public static String getContentType(URLConnection conn, String deft) {
        String encoding = deft;
        String ct = conn.getContentType();    
        if (ct != null) {
            int idx = ct.indexOf(';'); 
            if (idx == -1) encoding = ct;
            else encoding = ct.substring(idx+1);
        }
        int idx = encoding.indexOf('=');
        if (idx != -1 ) encoding = encoding.substring(idx+1);
        encoding = encoding.toUpperCase().trim();
        return encoding;
    }
    
    public static String getContent(URLConnection conn) throws IOException {
        InputStream is = conn.getInputStream();
        InputStream resultingInputStream = is;
        
        String encoding = conn.getContentEncoding();
        if (encoding != null && encoding.equalsIgnoreCase("gzip")) {
            resultingInputStream = new GZIPInputStream(is);
        } else if (encoding != null && encoding.equalsIgnoreCase("deflate")) {
            resultingInputStream = new InflaterInputStream(is, new Inflater(true));
        }

        return IOUtils.toString(resultingInputStream, getContentType(conn, "UTF-8"));   
    }
    
    public HttpURLConnection buildConnection(String url) throws MalformedURLException, IOException {
        HttpURLConnection httpConn =  
            (HttpURLConnection)new URL(url).openConnection();
        httpConn.addRequestProperty("User-Agent", 
                "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6");
        String authToken = Base64.encode(getUsername()+":"+getPassword());
        httpConn.addRequestProperty("Authorization", "Basic "+authToken);
        httpConn.addRequestProperty("Accept-Encoding", "gzip,deflate");
        return httpConn;
    }
    
    public String getRecentPosts() throws IOException {
        String url = baseURLV1 + pathRecentPosts;
        return getPosts(url);
    }
    
    public String getRecentPosts(int count) throws IOException {
        String url = baseURLV1 + pathRecentPosts + "?count="+count;
        return getPosts(url);
    }
    
    public String getAllPosts() throws IOException {
        String url = baseURLV1 + pathAllPosts;
        return getPosts(url);
    }
    
    public Collection<Link> cvtPosts2Link(String posts)
            throws SAXException, IOException, ParserConfigurationException {
        Collection<Link> links = new LinkedList<Link>();
        DefaultHandler handler = new DeliciousPostSAXHandler(links);
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
        parser.parse(new InputSource(new StringReader(posts)), handler);
        return links;
    }
    
    public String getPosts(String url) throws IOException {
        HttpURLConnection httpConn = buildConnection(url);
        httpConn.connect();

        if (httpConn.getResponseCode() >= 200 && httpConn.getResponseCode() < 300) {
            String resp = getContent(httpConn);
            httpConn.disconnect();
            return resp;
        } else {
            httpConn.disconnect();
            throw new ConnectException("Connection failed!");
        }
    }
    
    /*
     * Each line in the <code>file</code> contains a url to be deleted.
     */
    public void delPosts(File file)
            throws IOException, SAXException, ParserConfigurationException {
        LineIterator lineIt = FileUtils.lineIterator(file, "UTF-8");
        while (lineIt.hasNext()){
            String line = lineIt.nextLine();
            System.out.println("deleting link:" + line);
            if (line != null && line.trim().length() > 0) {
                boolean result = delPost(line.trim());
                System.out.println("result:" + (result?"succeeded":"failed"));
                try {
                    Thread.sleep(1200); // prevent it from being banned
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public void delPosts(String urls[])
            throws IOException, SAXException, ParserConfigurationException {
        boolean succ;
        for(int i = 0; i < urls.length; ++i) { 
            succ = delPost(urls[i]);
            try {
                Thread.sleep(1200); // prevent it from being banned
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public boolean delPost (String url2del) throws IOException, SAXException, ParserConfigurationException {
        String delUrl = baseURLV1 + pathDelPost;
        delUrl += "?url="+URLEncoder.encode(url2del, "UTF-8");
        String resp = getPosts(delUrl);
        return checkRespNonPosts(resp);
    }

    private boolean checkRespNonPosts (String resp) {
        return checkRespNonPosts(resp, null);
    }
    
    /**
     * For actions which don't generate results, what we care about is whether
     * the action is successfully executed.
     * This method parses those responses.
     * 
     * @param resp
     * @return
     */
    private boolean checkRespNonPosts (String resp, StringBuilder sb) {
        // <result code="done" />
        // <result code="access denied"/>
        // etc.
        DocumentBuilder builder;
        try {
            builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
            Document doc = builder.parse(new InputSource(new StringReader(resp)));
            NodeList nl = doc.getElementsByTagName("result");
            if (nl.getLength() != 1)
                return false;
            else {
                String codeAtt = ((Element) nl.item(0)).getAttribute("code");
                if (sb != null) sb.append("Raw response:" + resp + "\nCode:" + codeAtt + "\n");
                if (codeAtt.equals("done"))
                    return true;
                else
                    return false;
            }
        } catch (ParserConfigurationException e) {
            if (sb != null) sb.append("Exception was thrown:\n" + getExceptionStackTrace(e));
            e.printStackTrace();
        } catch (SAXException e) {
            if (sb != null) sb.append("Exception was thrown:\n" + getExceptionStackTrace(e));
            e.printStackTrace();
        } catch (IOException e) {
            if (sb != null) sb.append("Exception was thrown:\n" + getExceptionStackTrace(e));
            e.printStackTrace();
        }
        return false;
    }
    
    public static String getExceptionStackTrace (Exception e) {
        StringWriter sw = new StringWriter();
        PrintWriter pw = new PrintWriter(sw);
        e.printStackTrace(pw);
        pw.flush();
        return sw.getBuffer().toString();
    }
    
    public boolean login (StringBuilder outputMsg) {
        try {
            getRecentPosts(1);
            return true;
            // return checkRespNonPosts(resp, outputMsg);
        } catch (IOException e) {
            e.printStackTrace();
            if (outputMsg != null)
                outputMsg.append("Exception was thrown:\n" + getExceptionStackTrace(e));
            return false;
        }
    }
    
    public boolean login () {
        return login(null);
    }
    
    public static void main(String args[]) throws IOException, ParserConfigurationException, SAXException {
        String username, password;
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        System.out.print("Delicious username:");
        username = br.readLine();
        System.out.print("Delicious password:");
        password = br.readLine();
        
        DeliciousPostsMgrV1 delMgr = new DeliciousPostsMgrV1(username, password);
        // String resp = delMgr.getRecentPosts();
        // delMgr.getAllPosts();
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getUsername() {
        return username;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public String getPassword() {
        return password;
    }
}

class DeliciousPostSAXHandler extends DefaultHandler {
    public Collection<Link> linksRef;
    public DeliciousPostSAXHandler(Collection<Link> links) {
        linksRef = links;
    }
    
    //2008-05-18T19:37:32Z
    public static DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
    
    public void startElement (String uri, String localName,
            String qName, Attributes atts) {
        String href = atts.getValue("href");
        String desp = atts.getValue("description");
        String tags[] = atts.getValue("tag").split("\\s+");
        
        if (!qName.equals("post")) return;
        
        Link link;
        try {
            link = new Link(new URL(href));
            link.setProperty("description", desp);
            link.setProperty("tags", tags);
            Date time = df.parse(atts.getValue("time"));
            link.setProperty("addTime", time);
            linksRef.add(link);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println(href+"\n"+desp);
    }
}
